
package com.android.superobstacleavoider;

import java.util.ArrayList;
import java.util.List;

//import com.badlogic.gdx.math.Matrix4;


public class BoundingBox{        
        final Vector3 crn[] = new Vector3[8];
        public final Vector3 min = new Vector3();
        public final Vector3 max = new Vector3();
        final Vector3 cnt = new Vector3();
        final Vector3 dim = new Vector3();

        /**
         * @return the center of the bounding box
         */
        public Vector3 getCenter () {
                return cnt;
        }

        protected void updateCorners () {

                crn[0].set(min.X, min.Y, min.Z);
                crn[1].set(max.X, min.Y, min.Z);
                crn[2].set(max.X, max.Y, min.Z);
                crn[3].set(min.X, max.Y, min.Z);
                crn[4].set(min.X, min.Y, max.Z);
                crn[5].set(max.X, min.Y, max.Z);
                crn[6].set(max.X, max.Y, max.Z);
                crn[7].set(min.X, max.Y, max.Z);
        }

        /**
         * @return the corners of this bounding box
         */
        public Vector3[] getCorners () {
                updateCorners();
                return crn;
        }

        /**
         * @return The dimensions of this bounding box on all three axis
         */
        public Vector3 getDimensions () {
                return dim;
        }

        /**
         * @return The minimum vector
         */
        public Vector3 getMin () {
                return min;
        }

        /**
         * @return The maximum vector
         */
        public synchronized Vector3 getMax () {
                return max;
        }

        /**
         * Constructs a new bounding box with the minimum and maximum vector set to zeros.
         */
        public BoundingBox () {
               
                for (int l_idx = 0; l_idx < 8; l_idx++)
                        crn[l_idx] = new Vector3();
                clr();
        }

        /**
         * Constructs a new bounding box from the given bounding box.
         * 
         * @param bounds The bounding box to copy
         */
        public BoundingBox (BoundingBox bounds) {
               
                for (int l_idx = 0; l_idx < 8; l_idx++)
                        crn[l_idx] = new Vector3();
                this.set(bounds);
        }

        /**
         * Constructs the new bounding box using the given minimum and maximum vector.
         * 
         * @param minimum The minimum vector
         * @param maximum The maximum vector
         */
        public BoundingBox (Vector3 minimum, Vector3 maximum) {
                
                for (int l_idx = 0; l_idx < 8; l_idx++)
                        crn[l_idx] = new Vector3();
                this.set(minimum, maximum);
        }

        /**
         * Sets the given bounding box.
         * 
         * @param bounds The bounds.
         * @return This bounding box for chaining.
         */
        public BoundingBox set (BoundingBox bounds) {
             
                return this.set(bounds.min, bounds.max);
        }

        /**
         * Sets the given minimum and maximum vector.
         * 
         * @param minimum The minimum vector
         * @param maximum The maximum vector
         * @return This bounding box for chaining.
         */
        public BoundingBox set (Vector3 minimum, Vector3 maximum) {
                min.set(minimum.X < maximum.X ? minimum.X : maximum.X, minimum.Y < maximum.Y ? minimum.Y : maximum.Y,
                        minimum.Z < maximum.Z ? minimum.Z : maximum.Z);
                max.set(minimum.X > maximum.X ? minimum.X : maximum.X, minimum.Y > maximum.Y ? minimum.Y : maximum.Y,
                        minimum.Z > maximum.Z ? minimum.Z : maximum.Z);
                cnt.set(min).add(max).mul(0.5f);
                dim.set(max).sub(min);
               
                return this;
        }

        /**
         * Sets the bounding box minimum and maximum vector from the given points.
         * 
         * @param points The points.
         * @return This bounding box for chaining.
         */
        public BoundingBox set (Vector3[] points) {
                this.inf();
                for (Vector3 l_point : points)
                        this.ext(l_point);
                
                return this;
        }
        
        public BoundingBox set(float[] points)
        {
        	this.inf();
        	ArrayList<Vector3> temp = new ArrayList<Vector3>();
        	for(int i = 0; i < points.length; i += 3)
        	{
        		temp.add(new Vector3(points[i], points[i+1], points[i+2]));
        	}
        	
        	return this.set(temp);
        }

        /**
         * Sets the bounding box minimum and maximum vector from the given points.
         * 
         * @param points The points.
         * @return This bounding box for chaining.
         */
        public BoundingBox set (List<Vector3> points) {
                this.inf();
                for (Vector3 l_point : points)
                        this.ext(l_point);
              
                return this;
        }

        /**
         * Sets the minimum and maximum vector to positive and negative infinity.
         * 
         * @return This bounding box for chaining.
         */
        public BoundingBox inf () {
                min.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
                max.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
                cnt.set(0, 0, 0);
                dim.set(0, 0, 0);
               
                return this;
        }

        /**
         * Extends the bounding box to incorporate the given {@link Vector3}.
         * 
         * @param point The vector
         * @return This bounding box for chaining.
         */
        public BoundingBox ext (Vector3 point) {
               
                return this.set(min.set(min(min.X, point.X), min(min.Y, point.Y), min(min.Z, point.Z)),
                        max.set(Math.max(max.X, point.X), Math.max(max.Y, point.Y), Math.max(max.Z, point.Z)));
        }

        /**
         * Sets the minimum and maximum vector to zeros
         * 
         * @return This bounding box for chaining.
         */
        public BoundingBox clr () {
              
                return this.set(min.set(0, 0, 0), max.set(0, 0, 0));
        }

        /**
         * Returns wheter this bounding box is valid. This means that min != max and min < max.
         * 
         * @return True in case the bounding box is valid, false otherwise
         */
        public boolean isValid () {
                return !(min.X == max.X && min.Y == max.Y && min.Z == max.Z);
        }

        /**
         * Extends this bounding box by the given bounding box.
         * 
         * @param a_bounds The bounding box
         * @return This bounding box for chaining.
         */
        public BoundingBox ext (BoundingBox a_bounds) {
               
                return this.set(min.set(min(min.X, a_bounds.min.X), min(min.Y, a_bounds.min.Y), min(min.Z, a_bounds.min.Z)),
                        max.set(max(max.X, a_bounds.max.X), max(max.Y, a_bounds.max.Y), max(max.Z, a_bounds.max.Z)));
        }

        /**
         * Multiplies the bounding box by the given matrix. This is achieved by multiplying the 8 corner points and then calculating
         * the minimum and maximum vectors from the transformed points.
         * 
         * @param matrix The matrix
         * @return This bounding box for chaining.
         */
       

        /**
         * Returns wheter the given bounding box is contained in this bounding box.
         * @param bounds The bounding box
         * @return Wheter the given bounding box is contained
         */
        public boolean contains (BoundingBox bounds) {
                if (!isValid()) return true;
                if (min.X > bounds.max.X) return false;
                if (min.Y > bounds.max.Y) return false;
                if (min.Z > bounds.max.Z) return false;
                if (max.X < bounds.min.X) return false;
                if (max.Y < bounds.min.Y) return false;
                if (max.Z < bounds.min.Z) return false;
                return true;
        }

        /**
         * Returns wheter the given vector is contained in this bounding box.
         * @param v The vector
         * @return Wheter the vector is contained or not.
         */
        public boolean contains (Vector3 v) {
                if (min.X > v.X) return false;
                if (max.X < v.X) return false;
                if (min.Y > v.Y) return false;
                if (max.Y < v.Y) return false;
                if (min.Z > v.Z) return false;
                if (max.Z < v.Z) return false;

                return true;
        }

        public String toString () {
                return "[" + min + "|" + max + "]";
        }

        /**
         * Extends the bounding box by the given vector.
         * 
         * @param x The x-coordinate
         * @param y The y-coordinate
         * @param z The z-coordinate
         * @return This bounding box for chaining.
         */
        public BoundingBox ext (float x, float y, float z) {
               
                return this.set(min.set(min(min.X, x), min(min.Y, y), min(min.Z, z)),
                				max.set(max(max.X, x), max(max.Y, y), max(max.Z, z)));
        }

        static float min (float a, float b) {
                return a > b ? b : a;
        }

        static float max (float a, float b) {
                return a > b ? a : b;
        }
}
